<?php
/**
 * Created by Unknown
 *
 *            ┏┓　　  ┏┓+ +
 *           ┏┛┻━━━━━┛┻┓ + +
 *           ┃         ┃ 　
 *           ┃   ━     ┃ ++ + + +
 *          ████━████  ┃+
 *           ┃　　　　　 ┃ +
 *           ┃　　　┻　　┃
 *           ┃　　　　　 ┃ + +
 *           ┗━┓　　　┏━┛
 *             ┃　　　┃　　　　
 *             ┃　　　┃ + + + +
 *             ┃　　　┃    Code is far away from bug with the alpaca protecting
 *             ┃　　　┃ + 　　　　        神兽保佑,代码无bug
 *             ┃　　　┃
 *             ┃　　　┃　　+
 *             ┃     ┗━━━┓ + +
 *             ┃         ┣┓
 *             ┃ 　　　　　┏┛
 *             ┗┓┓┏━━┳┓┏━┛ + + + +
 *              ┃┫┫  ┃┫┫
 *              ┗┻┛  ┗┻┛+ + + +
 * User: MotherFucker
 * Date: 2016-08-01
 * Time: 上午 10:31
 */
namespace models\Redis;

use Yaf\Registry;
use Yaf\Exception;
use Predis\Client;
use ErrorMsg\AbstractErrorMsg as ErrMsg;

class RedisManager
{

    private $__client;

    public function __construct($options = [])
    {
        $this->__client = new Client(!empty($options) && is_array($options) ? $options : '');

    }

    public function getClient(){
        return $this->__client;
    }

    /**
     * 选择库
     * @param int $db_no
     */
    public function selectDb($db_no = 0){
        $this->__client->select($db_no);
    }

    /**
     * @param $key
     * @param $value
     * @param bool $check_key_is_exists     是否执行字段检查
     * @return int|mixed
     * @throws Exception
     */
    public function genString($key, $value,$check_key_is_exists = true){
        // key不能为空
        if(empty($key)){
            throw new Exception(ErrMsg::REDIS_KEY_IS_NOT_EMPTY,
                ErrMsg::REDIS_KEY_IS_NOT_EMPTY_NO);
        }

        return $check_key_is_exists === true ? $this->__client->setnx($key, json_encode($value, true)):
            $this->__client->set($key, json_encode($value, true));

    }

    public function getString($key){
        // key不能为空
        if(empty($key)){
            throw new Exception(ErrMsg::REDIS_KEY_IS_NOT_EMPTY,
                ErrMsg::REDIS_KEY_IS_NOT_EMPTY_NO);
        }

        return $this->__client->exists($key) ? json_decode($this->__client->get($key), true) : [];
    }

    /**
     * 删除string
     * @param $key
     * @return array|int
     * @throws Exception
     */
    public function delSting($key){
        if(empty($key)){
            throw new Exception(ErrMsg::REDIS_KEY_IS_NOT_EMPTY,
                ErrMsg::REDIS_KEY_IS_NOT_EMPTY_NO);
        }
        return $this->__client->exists($key) ? $this->__client->del($key) : [];
    }

    /**
     * 生成redis hash
     * @param string $hash_key hash_key
     * @param string $value 对应存入的值，可为int，string，array
     * @param string $key 对应的键，可为空，为空时，value必须是数组
     * @param bool $check_key_is_exists 默认true，意为：如果key存在，则不修改；反之不管key存不存在，都修改
     * @return int|mixed
     *          $value如果是array保存成功后会return一个Predis/Response/Status对象，该对象下有个方法getPayload若输出一个OK表示保存成功
     *          $value若是一个int或string，会return一个int(0或1)，key已经存在为1，反之为0
     * @throws Exception
     */
    public function genHash($hash_key = '', $value = '', $key = '', $check_key_is_exists = true){
        // hash_key不能为空

        if(empty($hash_key)){
            throw new Exception(ErrMsg::REDIS_HASH_KEY_IS_NOT_EMPTY,
                ErrMsg::REDIS_HASH_KEY_IS_NOT_EMPTY_NO);
        }

        // key跟value都不存在，或key不存在且value不为数组时，提示错误
        if((empty($key) && empty($value)) || (empty($key) && !is_array($value))){
            throw new Exception(ErrMsg::REDIS_KEY_OR_VALUE_IS_NOT_EMPTY,
                ErrMsg::REDIS_KEY_OR_VALUE_IS_NOT_EMPTY_NO);
        }else{
            if(is_array($value)){
//                exit(json_encode($value));

                return $this->__client->hmset($hash_key, $value);
            }else if(is_string($value) or is_integer($value)){
                return $check_key_is_exists === false ?
                    $this->__client->hset($hash_key, $key, $value) : $this->__client->hsetnx($hash_key, $key, $value);
            }else{
                throw new Exception(ErrMsg::REDIS_VALUE_ILLEGAL_PARAMETER_TYPE,
                    ErrMsg::REDIS_VALUE_ILLEGAL_PARAMETER_TYPE_NO);
            }
        }
    }

    /**
     * 读取hash的长度
     * @param string $hash_key hash_key
     * @return int
     * @throws Exception
     */
    public function getLenHash($hash_key = ''){
        // hash_key不能为空
        if(empty($hash_key)){
            throw new Exception(ErrMsg::REDIS_HASH_KEY_IS_NOT_EMPTY,
                ErrMsg::REDIS_HASH_KEY_IS_NOT_EMPTY_NO);
        }

        return $this->__client->hlen($hash_key);
    }

    /**
     * 读取hash
     * @param string $hash_key hash_key
     * @param mixed $key 指定hash_key下的某个key:可为空,为空时表示要读取整个hash_key下的所有值;可为字符串，既取出一个key;可为数组，既取出若干个
     * @return array|bool
     * @throws Exception
     */
    public function getHash($hash_key = '', $key = ''){
        // hash_key不能为空
        if(empty($hash_key)){
            throw new Exception(ErrMsg::REDIS_HASH_KEY_IS_NOT_EMPTY,
                ErrMsg::REDIS_HASH_KEY_IS_NOT_EMPTY_NO);
        }

        // key不为空，且key存在于hash_key中，则返回对应的值；当key不存在于hash_key中，返回false
        if($key && is_string($key)){
            Registry::get('proto_log')->info(json_encode([$hash_key, $key, $this->__client->hget($hash_key, $key)]));
            return $this->__client->hexists($hash_key, $key) ? $this->__client->hget($hash_key, $key) : false;
        }
        // key不为空，且为数组时，判断key里的每个值是否都存在于hash_key中，若存在返回对应的只；反之返回false
        else if($key && is_array($key)){
            $keys = $this->__client->hkeys($hash_key);
            $return_array = [];
            foreach ($key as $index => $real_key){
                $return_array[$real_key] = in_array($real_key, $keys) ?
                    $this->__client->hget($hash_key, $real_key) : false;
            }
            return $return_array;
        }
        // key为空，返回整个hash_key下的数组
        else{
            return $this->__client->hgetall($hash_key);
        }
    }

    /**
     * 删除hash_key下的指定key
     * @param string $hash_key
     * @param mixed $key 要删除的key，可为string或array
     * @return bool|int
     * @throws Exception
     */
    public function delHash($hash_key = '', $key = ''){
        // hash_key不能为空
        if(empty($hash_key)){
            throw new Exception(ErrMsg::REDIS_HASH_KEY_IS_NOT_EMPTY,
                ErrMsg::REDIS_HASH_KEY_IS_NOT_EMPTY_NO);
        }
        $real_key_arr = [];
        if(!$key){
            $keys = $this->__client->hkeys($hash_key);
            foreach ($keys as $index => $real_key) {
                $real_key_arr[] = $real_key;
            }
//            throw new Exception(
//                ErrMsg::REDIS_HASH_KEY_KEY_IS_NOT_EMPTY, ErrMsg::REDIS_HASH_KEY_KEY_IS_NOT_EMPTY_NO);
        }else if(is_array($key)){
            $keys = $this->__client->hkeys($hash_key);
            foreach ($key as $index => $real_key) {
                if (in_array($real_key, $keys)) {
                    $real_key_arr[] = $real_key;
                }
            }
        }else{
            if($this->__client->hexists($hash_key, $key)){
                $real_key_arr[] = $key;
            }
        }

        return !empty($real_key_arr) ? $this->__client->hdel($hash_key, $real_key_arr) : false;
    }

    /**
     * 修改hash_key下的指定key的值
     * @param string $hash_key
     * @param array $modify_arr 要增加数值的键值对 ['key' => 1, 'key1' => 1.1, 'key3' => 'value3', ... ]
     * @return array 修改过后的值整个return回去
     * @throws Exception
     */
    public function modifyHash($hash_key = '', $modify_arr = []){
        // hash_key不能为空
        if(empty($hash_key)){
            throw new Exception(ErrMsg::REDIS_HASH_KEY_IS_NOT_EMPTY,
                ErrMsg::REDIS_HASH_KEY_IS_NOT_EMPTY_NO);
        }

        // 传入的参数不合法
        if(!$modify_arr || !is_array($modify_arr)){
            throw new Exception(
                ErrMsg::REDIS_HASH_KEY_KEY_IS_NOT_EMPTY, ErrMsg::REDIS_HASH_KEY_KEY_IS_NOT_EMPTY_NO);
        }

        // hash_key不存在
        if($this->__client->hlen($hash_key) === 0){
            throw new Exception(
                ErrMsg::REDIS_HASH_KEY_NOT_EXISTS, ErrMsg::REDIS_HASH_KEY_NOT_EXISTS_NO);
        }

        $keys = $this->__client->hkeys($hash_key);
        $values = $this->__client->hvals($hash_key);
        foreach ($modify_arr as $key => $update_data) {
            if (in_array($key, $keys)) {
                $key_index = array_search($key, $keys);
                $value = $values[$key_index];
                if(is_integer($value) && is_integer($update_data)){
                    $this->__client->hincrby($hash_key, $key, $update_data);
                }else if(is_float($value) && is_float($update_data)){
                    $this->__client->hincrbyfloat($hash_key, $key, $update_data);
                }else{
                    $this->__client->hset($hash_key, $key, $update_data);
                }
            }
        }

        return $this->__client->hgetall($hash_key);
    }
    //    模糊搜索指定hash_key下的hash
    public function getHashKeys($hash_key = '',$value_key = ''){
        // hash_key不能为空
        if(empty($hash_key)){
            throw new Exception(ErrMsg::REDIS_HASH_KEY_IS_NOT_EMPTY,
                ErrMsg::REDIS_HASH_KEY_IS_NOT_EMPTY_NO);
        }

//        模糊搜索hash_key
        return $this->__client->keys($hash_key);
//        获取hash_key下所有key
//        return $this->__client->hkeys($hash_key);
    }


    /*public function cleanModifiedCache($cache_name = ''){
        $cache_name = !empty($cache_name) ? trim($cache_name) : '';
        if(empty($cache_name)){
            return false;
        }

        $this->__client->del([$cache_name]);
        return true;
    }*/

    /**
     * 获取导入的CSV文件内容
     * @param string $hashKey       //要获取的redis的hashkey
     * @param int $selectDb         //选择redis的库,默认10
     */
    public function getRedisSelectDbData($hashKey = '',$selectDb=10,$key = ''){
        Registry::get('rdm')->selectDb($selectDb);
        if(empty($key)){
            $redis_data=Registry::get('rdm')->getHash($hashKey);
        }else{
            $redis_data=Registry::get('rdm')->getHash($hashKey,$key);
        }
        return $redis_data;
    }

    /**
     * 设置参数过期时间
     * @param $key
     * @param int $time
     * @return bool
     */
    public function setExpire($key,$time = 10){
        return $this->__client->expire($key,$time);
    }

    public function test(){
        return get_class_methods($this->__client);
    }
}
